package com.kk.ipresolution.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;

/**
 * the class of demo
 *
 * <p>
 * .
 *
 * @author little_lunatic
 * @date 2023-07-11
 */
@Slf4j
public class IPUtil {

    private static final String UNKNOWN = "unknown";

    // IP地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";

    // 未知地址
    public static final String UNKNOWN_ADDRESS = "XX XX";

    private IPUtil() {
    }

    /**
     * 获取 IP地址
     * 使用 Nginx等反向代理软件， 则不能通过 request.getRemoteAddr()获取 IP地址
     * 如果使用了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP地址，
     * X-Forwarded-For中第一个非 unknown的有效IP字符串，则为真实IP地址
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        log.info("IP: {}", ip);
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
            log.info("Proxy-Client-IP: {}", ip);
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
            log.info("WL-Proxy-Client-IP: {}", ip);
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
            log.info("request.getRemoteAddr: {}", ip);
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    /**
     * 离线解析IP地址
     *
     * @param ip
     * @return
     */
    public static String offlineResolutionIp(String ip) {
        log.info("==========离线解析IP地址===========");
        String dbPath = "src/main/resources/ip2region.xdb";
        // 1、从 dbPath 加载整个 xdb 到内存。
        byte[] cBuff;
        try {
            cBuff = Searcher.loadContentFromFile(dbPath);
        } catch (Exception e) {
            log.error("failed to load content from {}", dbPath, e);
            return null;
        }

        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
        Searcher searcher;
        try {
            searcher = Searcher.newWithBuffer(cBuff);
        } catch (Exception e) {
            log.info("failed to create content cached searcher: {}", e.getMessage(), e);
            return null;
        }
        // 3、查询
        try {
            String region = searcher.searchByStr(ip);
            log.info("{} ---> {}", ip, region);
            String[] strings = region.split("\\|");
            String country = strings[0];
            String provence = strings[2];
            String city = strings[3];
            log.info("==========离线解析IP地址结束===========");
            log.info("                                   ||");
            return country + provence + city;
        } catch (Exception e) {
            log.info("failed to search {}", ip, e);
            return UNKNOWN;
        }
    }


    /**
     * 在线解析IP地址
     *
     * @param ip
     * @return
     */
    public static String onlineResolutionIp(String ip) {
        log.info("==========在线解析IP地址===========");
        try {
            String rspStr = sendGetRequest(IP_URL, "ip=" + ip + "&json=true", "GBK");
            if (StrUtil.isBlank(rspStr)) {
                log.error("获取地理位置异常 {}", ip);
                return UNKNOWN_ADDRESS;
            }
            JSONObject obj = JSONUtil.parseObj(rspStr);
            String region = obj.getStr("pro");
            String city = obj.getStr("city");
            String res = String.format("%s %s", region, city);
            res = StrUtil.isBlank(res) ? UNKNOWN_ADDRESS : res;
            log.info("==========在线解析IP地址结束===========");
            log.info("                                   ||");
            return res;
        } catch (Exception e) {
            log.error("获取地理位置异常 {}", ip);
            return UNKNOWN_ADDRESS;
        }
    }

    public static String sendGetRequest(String url, String param, String contentType) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            log.info("sendGetRequest - {}", urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept", "*/*");
            connection.setRequestProperty("connection", "Keep-Alive");
            connection.setRequestProperty("User-Agent", "Mozilla/4.0 compatible; MSIE 5.0;Windows NT; DigExt)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("receive - {}", result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendGetRequest ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendGetRequest SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendGetRequest IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendGetRequest Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }
}
